import numpy as np
import pandas as pd
from faker import Faker

class DataGenerator:
    def __init__(self, seed=42):
        self.seed = seed
        self.steps = None
        self.fake = Faker()

    def series_data_generator(self, **kwargs):
        fn_list = [
            self._random_integer_data,
            self._trending_integer_data,
            self._zero_integer_data,
            self._wave_integer_data
        ]
        product_data = kwargs.get('product_data')
        steps_list = self.series_integer_data(self.seed, **kwargs)
        min_sales=kwargs.get('min_sales', 0)
        max_sales=kwargs.get('max_sales', 200)
        all_series_data = {}
        for product_id in product_data['id']:
            seed = hash(product_id)
            series_list = []
            for steps in steps_list:
                fn = np.random.choice(fn_list)
                result = fn(seed, steps, min_sales, max_sales)
                series_list.append(result)
            all_series_data[product_id] = np.hstack(series_list)
        series_data_df = pd.DataFrame(all_series_data)
        return series_data_df

    def series_integer_data(self, seed):
        if self.steps is None:
            raise ValueError("steps must be set before calling series_integer_data, use series_date_data first.")
        else:
            # 随机选择段数，范围为 1 到 5
            num_segments = np.random.randint(1, 6)
            # 计算平均步长
            avg_step = self.steps // num_segments
            # 计算剩余步长
            remainder = self.steps % num_segments

            # 初始化段的起始索引
            steps_list = [avg_step + (1 if i < remainder else 0) for i in range(num_segments)]
        return steps_list


    def _random_integer_data(self, seed, steps, min_sales: int, max_sales: int):
        # 设置随机种子
        np.random.seed(seed)
        # 生成从 min_sales 到 max_sales 的整型数据序列，长度为 steps
        return np.random.randint(min_sales, max_sales + 1, steps)

    def _trending_integer_data(self, seed, steps, min_sales: int, max_sales: int):
        # y=ax+b
        # 设置随机种子
        np.random.seed(seed)
        # 生成斜率和截距
        a = np.random.randn()
        b = np.random.randint(min_sales, max_sales)
        # 生成线性趋势数据
        trend = a * np.arange(steps) + b
        # 确保数据在 [min_sales, max_sales] 范围内
        trend = np.clip(trend, min_sales, max_sales)
        # 转换为整数
        trend = trend.astype(int)
        return trend

    def _zero_integer_data(self, seed, steps, min_sales: int, max_sales: int):
        # 设置随机种子
        np.random.seed(seed)
        # 生成全零数组，长度为 self.steps
        zero_data = np.zeros(steps, dtype=int)
        # 随机选择部分位置
        random_indices = np.random.choice(steps, size=int(self.steps * 0.2), replace=False)
        # 将这些位置的值设置为不大于 5 的随机整数
        zero_data[random_indices] = np.random.randint(1, 6, size=len(random_indices))
        return zero_data

    def _wave_integer_data(self, seed, steps, min_sales: int, max_sales: int):
        # 设置随机种子
        np.random.seed(seed)
        # 生成随机周期，范围在 5 到 20 之间
        period = np.random.randint(5, 21)
        # 生成随机振幅，范围在 1 到 10 之间
        amplitude = np.random.randint(1, 11)
        # 生成随机相位，范围在 0 到 2π 之间
        phase = np.random.uniform(0, 2 * np.pi)
        # 生成随机垂直偏移，范围在 min_sales 到 max_sales 之间
        vertical_shift = np.random.randint(min_sales, max_sales + 1)
        # 生成波动数据
        wave = amplitude * np.sin(2 * np.pi * np.arange(steps) / period + phase) + vertical_shift
        # 确保数据在 [min_sales, max_sales] 范围内
        wave = np.clip(wave, min_sales, max_sales)
        # 转换为整数
        wave = wave.astype(int)
        return wave

    def _fourier_integer_data(self, seed, steps, min_sales: int, max_sales: int):
        # 生成多次随机的波动整数数据并叠加
        num_generations = np.random.randint(1, 6)  # 随机生成次数，范围为 1 到 5
        all_wave_data = np.zeros(steps, dtype=int)
        for _ in range(num_generations):
            wave_data = self._wave_integer_data(seed, min_sales, max_sales)
            all_wave_data += wave_data

        return all_wave_data


    def series_date_data(self, start: str, end: str):
        # 使用 numpy 的 arange 函数生成从 start 到 end 的连续日期
        dates = np.arange(np.datetime64(start), np.datetime64(end))
        self.steps = len(dates)
        return dates

    def product_data_generator(self, product_count=30, category_count=50):
        # id
            # product_code
            # product_category
            # product_price
            # product_size

        data = []

        product_categories = [self.fake.bothify(text='??###') for _ in range(category_count)]
        
        

        for product_category in product_categories:
            prd_cnt_bias = np.random.randint(-category_count/2, 20)
            for _ in range(product_count+prd_cnt_bias):
                # 生成 UUID 并转换为整数
                uuid_obj = self.fake.uuid4()
                uuid_int = int(uuid_obj.replace('-', ''), 16)
                product_code = f"{product_category}{self.fake.bothify(text='####')}"
                product = {
                "id": uuid_int,
                "product_code": product_code,
                "product_category": product_category,
                "product_price": round(self.fake.pyfloat(left_digits=2, right_digits=2, positive=True), 2),
                "product_size": self.fake.random_element(elements=('Small', 'Medium', 'Large')),
                "product_season": self.fake.random_element(elements=('Summer', 'Winter', 'Allday', 'Festival'))
            }
            data.append(product)
        # 将生成的数据转换为 pandas.DataFrame
        return pd.DataFrame(data)

        


